package com.pap.oauth.config;

import com.pap.oauth.authentication.mobile.SmsCodeAuthenticationFilter;
import com.pap.oauth.authentication.mobile.SmsCodeAuthenticationProvider;
import com.pap.oauth.authentication.mobile.SmsCodeLoginFailureHandler;
import com.pap.oauth.authentication.mobile.SmsCodeLoginSuccessHandler;
import com.pap.oauth.authentication.mobile.config.SmsCodeAuthenticationSecurityConfig;
import com.pap.oauth.config.security.PapUserDetailsService;
import com.pap.oauth.config.security.external.param.PapUserDetailsAuthenticationHandler;
import com.pap.oauth.config.security.external.param.PapUserDetailsAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;

/**
 *  注意这里存在两个 configure(AuthenticationManagerBuilder auth) 方法
 *
 *      用户信息存储内存的方法（auth.inMemoryAuthentication()）已经被注释掉，将用户信息的查询存放至 PapUserDetailsService 文件中，
 *          这样用户信息将会被统一管理。  保证 security 和 oauth 两个部分的用户数据 被统一管理。
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     *  @Deprecated 取消自定义的 userDetailsService， 而改为在 PapUserDetailsAuthenticationProvider 中的支持。
      */
//    @Autowired
//    private PapUserDetailsService userDetailsService;

    /**
     *  短信登录配置：
     *  url : /authentication/mobile?mobile=13800138000
     *
     *  @Deprecated 取消对 smsCode 的单独支持，修改为 扩充登录过程中的请求参数。
     */
//    @Autowired
//    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
//
//    @Autowired
//    private SmsCodeLoginSuccessHandler smsCodeLoginSuccessHandler;
//
//    @Autowired
//    private SmsCodeLoginFailureHandler smsCodeLoginFailureHandler;

    @Autowired
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> papWebAuthenticationDetailsSource;

    /**
     * 登录验证中增加额外参数
     * @return
     */
    @Bean
    public PapUserDetailsAuthenticationHandler papUserDetailsAuthenticationHandler(){
        PapUserDetailsAuthenticationHandler authenticationHandler = new PapUserDetailsAuthenticationHandler();
        return authenticationHandler;
    }

    /**
     * 登录验证中增加额外参数
     * @return
     */
    @Bean
    public PapUserDetailsAuthenticationProvider papUserDetailsAuthenticationProvider(){
        PapUserDetailsAuthenticationProvider provider = new PapUserDetailsAuthenticationProvider(papUserDetailsAuthenticationHandler());
        return provider;
    }


    /**
     *注册用户的获取逻辑
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                // 修改认证提供，增加额外参数
                .authenticationProvider(papUserDetailsAuthenticationProvider());
    }


    /**
     *注册用户的获取逻辑
     */
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
//    }

    /**
     * 注意 role 部分，按照约定不能以 ROLE_ 开头。
     * PS： 备注说明，建议不删除此方法，供代码说明使用，详见类注释。
     * @param auth
     * @throws Exception
     */
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
//
//        auth.inMemoryAuthentication()
//                .withUser("pap").password(encoder.encode("pap")).roles("PAP")
//                .and()
//                .withUser("alexgaoyh").password(encoder.encode("alexgaoyh")).roles("ALEXGAOYH","PAP");
//    }


    /**
     *  HTTP请求安全处理
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.
                formLogin()
                // 增加自定义的 浏览器 授权，这里的方法
                .authenticationDetailsSource(papWebAuthenticationDetailsSource)
                .and()
                .requestMatchers()
                .antMatchers("/login","/oauth/authorize")
                .and()
                .authorizeRequests().antMatchers(
                "/**/*.js",
                "/**/*.css",
                "/**/*.jpg",
                "/**/*.png",
                "/**/*.woff2")
                .permitAll()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                // 关闭 Cross-site request forgery跨站请求伪造，
                .and().csrf().disable();
        // 取消对 smsCode 的单独支持，改为扩充登录请求过程中的参数。
        // http.apply(smsCodeAuthenticationSecurityConfig);
    }

    /**
     * 需要配置这个支持password模式
     *  同时在 Oauth2Conig 中进行注入
     * support password grant type
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}
